Purpose is to create a simple html generator for Python. Few other generators have been available since decade but they all seems to have small caveats. However combining features I've managed to create a library that fits better to my own projects.
Problems:
<my-tag dc:attr="" />Other requirements:
As simple implementation as possible, no need for complicated page generation methods, just basic functionality. Extending tags for structures like table and lists. Nesting tags and giving attribute names should be clean and intuitive. Pythonic.
In [1]:
class TAG(object):
def __init__(self, *args, **kw):
pass
def getName(self):
pass
def setName(self, name):
pass
def getAttribute(self, key):
pass
def setAttribute(self, key, value):
pass
def rcontent(self, item):
pass
def content(self, item):
pass
def prepend(self, item):
pass
def append(self, item):
pass
In [2]:
class htmlHelper(object):
def __getattr__(self, tag):
pass
In [3]:
def table(*args, **kw):
global helper
class table(type(helper.table())):
def __init__(self, *args, **kw):
pass
def addCaption(self, caption, **kw):
pass
def addColGroup(self, *cols, **kw):
pass
def addHeadRow(self, *trs, **kw):
pass
def addFootRow(self, *trs, **kw):
pass
def addBodyRow(self, *trs, **kw):
pass
def addBodyRows(self, *trs, **kw):
pass
return table(*args, **kw)
helper = htmlHelper()
In [4]:
from tagpy import helper as h
In [5]:
# introducing the main flow of the nesting tags
print h.html(h.head(h.title("Simple html document")), h.body("Content"))
In [6]:
# a tag without content will be output as a short tag form
print h.br()
In [7]:
# if you pass empty string on tag content, closing tag will be generated
print h.script('')
In [8]:
# content can be a string, a numeric or other tag elements
print h.h1("Header ", 1, h.span(".1"))
In [9]:
# providing other content can yield unexpected results because all will be string normalized
print h.div([0,1], {'k': h.b()})
In [10]:
# content can be callable
print h.p(h.br)
In [11]:
# as said, content can be callable
def ul():
return h.ul(h.li)
print h.div(ul)
In [12]:
# adding more content inside the element
# special operator here << is same as tag.content method
h1 = h.h1('Header')
h1 << " 1."
h1 << h.span("2")
print h1
In [13]:
# adding more content inside the element
# special operator here >> is same as tag.rcontent method (reverse side content)
h1 = h.h1('Header')
h1 >> ".2 "
h1 >> h.span("1")
print h1
In [14]:
# see also
print h.a() << h.b() << h.c()
print h.a() >> h.b() >> h.c()
In [15]:
# adding more content outside the element, right side
h1 = h.h1()
h1 += h.h2() #h1 = h1 + h.h2()
h1 += h.h3() #h1 = h1 + h.h3()
print h1
In [16]:
# adding more content outside the element, left side
h1 = h.h1()
h1 = h.h2() + h1
h1 = h.h3() + h1
print h1
In [17]:
# chain add elements
print h.h1() + h.h2() + h.h3()
# note that adding instance to the same instance causes recursive loop on string normalization
#h1 = h.h1()
#print h1 + h1 + h1 -> Runtime error
In [18]:
# chain arguments
print h.h1(h.span(), h.span(), h.span())
In [19]:
# chain arguments by list
print h.h1(*[h.span, h.span, h.span])
In [20]:
# add attributes
print h.div(id="container", title="Content container")
In [21]:
# add attributes by dictionary
print h.div(**{'id': "container", 'title':"Content container"})
In [22]:
# using python reserved words can be tackled with uppercase letters or capitalization
# h.del or h.tag(class="") doesn't work but gives parse error. instead use something like:
print h.DEL(Class="reserved")
In [23]:
# but if you really want uppercase tag names or attributes, you can always use setName and setAttribute methods
print h.tag().setName('DEL').setAttribute('Class', 'reserved')
In [24]:
# special attribute and tag names can be handled with setters.
# h.my-tag(dc:name = "special") doesn't work because of naming convention rules on python
# so you need to do:
print h.tag("content").setName('my-tag').setAttribute('dc:name', 'special')
In [25]:
# sure you can make tables with core table tags
tbl = h.table(CLASS="data")
tbl << h.thead(h.tr(*map(h.th, [1,2,3])))
tbl << h.tbody(*[h.tr(*map(h.td, ["1.%s"%i,"2.%s"%i,"3.%s"%i])) for i in [1,2,3]])
print tbl
In [26]:
from IPython.display import HTML
HTML(str(tbl))
Out[26]:
In [27]:
# but using special table factory function structuring table is easier
from tagpy import table
# initialize table
t = table(**{'id': 'data'})
# add caption title
t.addCaption('Caption')
columns = [{'style': 'background-color: red'},
{'style': 'background-color: green'},
{'style': 'background-color: blue'}]
# add column definitions
t.addColGroup(*[h.col(**attr) for attr in columns])
header = ['Column 1', 'Column 2', 'Column 3']
# add header row with column titles
t.addHeadRow(h.tr(*map(h.th, header)))
# add body rows
for i in range(1,3):
t.addBodyRow(h.tr(*map(h.td, ["1.%s"%i,"2.%s"%i,"3.%s"%i])))
# add separate bodies with rows
for i in range(3,5):
t.addBodyRows(h.tr(*map(h.td, ["1.%s"%i,"2.%s"%i,"3.%s"%i])), id='tbody%s'%i)
# add footer row
t.addFootRow(h.tr(h.td('footer', colspan="3")))
HTML(str(t))
Out[27]:
In [28]:
style = """
<style type="text/css">
table#data { margin: 1em auto; border-collapse: collapse; border: 0}
table#data caption { font-size: 1.2em; text-align: center; padding: 3px}
table#data th, table#data td { padding: .25em; border: 1px solid #000; font-family: sans-serif; color: white}
table#data th { color: #004900; font-weight: bold; text-align: left; }
table#data thead th { border-bottom: 3px double #000; background-color: #ddd; text-align: center; }
table#data tfoot td { border-top: 3px double #000; color: #fff; font-style: italic; font-size: .8em; text-align: center; background-color: brown}
table#data tbody th { color: #000; }
table#data #tbody3 {font-weight: bold;}
table#data #tbody4 {font-style: italic;}
</style>
"""
HTML(style)
Out[28]:
See test cases notebook.
Copyright (c) 2014 Marko Manninen
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.